#property link      "jolyonseddon@gmail.com"

// defines for managing trade orders
#define RETRYCOUNT    10
#define RETRYDELAY    500

#define LONG          1
#define SHORT         0
#define ALL           2

extern int MagicNumber = 201219;
extern double TradeSize = 0.01;
extern string IntCurrency = "XAUUSD";
extern string TimeFrame_Help = "0 = Current TF, 1=M1, 5=M5, 15=M15, ..., 1440=D1, 10080=W1, 43200=MN1";
extern int IntTimeFrame = 240;
extern string TrCurrency = "USDCHF";
extern int TrTimeFrame = 240;
extern int LSB = 0;
extern int Type = 1;
extern int LenTr = 20;
extern int LenInt = 40;
extern int Relate = 0;

extern bool Sunday.Session.Active = true;
extern string Sunday.Start.Time = "20:00"; 
extern string Sunday.End.Time = "23:59";
extern bool Monday.Session.Active = true;
extern string Monday.Start.Time = "00:00"; 
extern string Monday.End.Time = "23:59";
extern bool Tuesday.Session.Active = true;
extern string Tuesday.Start.Time = "00:00"; 
extern string Tuesday.End.Time = "23:59";
extern bool Wednesday.Session.Active = true;
extern string Wednesday.Start.Time = "00:00"; 
extern string Wednesday.End.Time = "23:59";
extern bool Thursday.Session.Active = true;
extern string Thursday.Start.Time = "00:00"; 
extern string Thursday.End.Time = "23:59";
extern bool Friday.Session.Active = true;
extern string Friday.Start.Time = "00:00"; 
extern string Friday.End.Time = "15:55";
extern int GMT.offHour=0;
extern bool CloseTrade.Out.Time = false;
extern int Slippage=2;

extern bool Text.Alert = true;
extern bool Sound.Alert = true;
extern string      sound_signal_buy          = "alert";           // a signal to buy
extern string      sound_signal_sell         = "alert2";          // a signal to sell
extern bool Email.Alert = true;

double MarkInd, InterInd;
int digitFactor = 1;
datetime lastTrActTime, lastIntActTime;
int orders.total = 0;
int orders.Long = 0;
int orders.Short = 0;

int init()
{   
   if(Bid<10 && Digits==5) { digitFactor=10;}
   if(Bid<10 && Digits==4) { digitFactor= 1;}

   if(Bid>10 && Digits==3) { digitFactor=10;}
   if(Bid>10 && Digits==2) { digitFactor= 1;} 

   Slippage *= digitFactor;  
  
 
   return(0);
}
//+------------------------------------------------------------------+
//| expert deinitialization function                                 |
//+------------------------------------------------------------------+
int deinit()
{
   switch( UninitializeReason() ) {
    case REASON_REMOVE:
    case REASON_ACCOUNT:
      break;
   }
   return(0);
}

int start()
{     
   CountOrders(TrCurrency);       
       
   bool isTradingDayTime = isTradingTime(); 
    if (!isTradingDayTime && CloseTrade.Out.Time && orders.total > 0)
    {
       ExitAll(LONG, TrCurrency);
       ExitAll(SHORT, TrCurrency);
    }
    
   if (iTime(TrCurrency, TrTimeFrame, 0) > lastTrActTime && (IntTimeFrame > TrTimeFrame || iTime(IntCurrency, IntTimeFrame, 0) > lastIntActTime))
   {
      if (Type == 0) 
      {
        InterInd=iClose(IntCurrency, IntTimeFrame, 1) - iClose(IntCurrency, IntTimeFrame, LenInt + 1);
        MarkInd=iClose(TrCurrency, TrTimeFrame, 1) - iClose(TrCurrency, TrTimeFrame, LenTr + 1);
      }
      if (Type == 1) 
      {
        InterInd=iClose(IntCurrency, IntTimeFrame, 1) - iMA(IntCurrency,IntTimeFrame,LenInt,0,MODE_SMA,PRICE_CLOSE,1);
        MarkInd=iClose(TrCurrency, TrTimeFrame, 1) - iMA(TrCurrency, TrTimeFrame, LenTr, 0,MODE_SMA,PRICE_CLOSE,1);
        
        //Print("InterInd:",InterInd, " MarkInd:", MarkInd, " iClose(IntCurrency, IntTimeFrame, 1):",iClose(IntCurrency, IntTimeFrame, 1), " iMA(IntCurrency,IntTimeFrame,LenInt,0,MODE_SMA,PRICE_CLOSE,1):",iMA(IntCurrency,IntTimeFrame,LenInt,0,MODE_SMA,PRICE_CLOSE,1));
      }           

      if (Relate==1)
      {
        if (InterInd>0 && MarkInd<0 && isTradingDayTime)
        {
            ExitAll(SHORT, TrCurrency);
            if (LSB>=0 && orders.Long == 0)
            {
                if (DoOpenOrder(TrCurrency, LONG, TradeSize, 0, 0, Blue, "") > 0)
                {
                     string alertStr = "Pair:" + Symbol() + ", T/F:" +  getTimeFrame(TrTimeFrame) + ", Direction: LONG, Price:" + DoubleToStr(Ask, Digits);
               
                     if (Text.Alert)
                      {
                          Alert(alertStr);
                          Print(alertStr);
                      }
                      if (Email.Alert)
                      {
                          SendMail("System 5 BUY to COVER and go LONG GBP at MARKET" , alertStr);  
                      }
                      if (Sound.Alert)
                      {
                          PlaySound(sound_signal_buy);
                      }  
                }
                else
                {
                    return;
                }
            }
        }
        if (InterInd<0 && MarkInd>0 && isTradingDayTime)
        {
            ExitAll(LONG, TrCurrency);
            if (LSB<=0 && orders.Short == 0)
            {
                if (DoOpenOrder(TrCurrency, SHORT, TradeSize, 0, 0, Red, "") > 0)
                {
                     alertStr = "Pair:" + Symbol() + ", T/F:" +  getTimeFrame(TrTimeFrame) + ", Direction: SHORT, Price:" + DoubleToStr(Bid, Digits);                   
                     if (Text.Alert)
                     {
                         Alert(alertStr);
                         Print(alertStr);
                     }
                     if (Email.Alert)
                     {
                         SendMail("System 5 SELL to CLOSE and go SHORT GBP at MARKET" , alertStr);  
                     }
                     if (Sound.Alert)
                     {
                         PlaySound(sound_signal_sell);
                     }    
                }
                else
                {
                    return;
                }
            }
        }
      }

      if (Relate==0)
      {
        if (InterInd<0 && MarkInd<0 && isTradingDayTime)
        {
            ExitAll(SHORT, TrCurrency);
            if (LSB>=0 && orders.Long == 0)
            {
                if (DoOpenOrder(TrCurrency, LONG, TradeSize, 0, 0, Blue, "") > 0)
                {
                     alertStr = "Pair:" + Symbol() + ", T/F:" +  getTimeFrame(TrTimeFrame) + ", Direction: LONG, Price:" + DoubleToStr(Ask, Digits);
               
                     if (Text.Alert)
                      {
                          Alert(alertStr);
                          Print(alertStr);
                      }
                      if (Email.Alert)
                      {
                          SendMail("System 5 BUY to COVER and go LONG GBP at MARKET" , alertStr);  
                      }
                      if (Sound.Alert)
                      {
                          PlaySound(sound_signal_buy);
                      }  
                }
                else
                {
                    return;
                }
            }
        }
        if (InterInd>0 && MarkInd>0 && isTradingDayTime)
        {
            ExitAll(LONG, TrCurrency);
            if (LSB<=0 && orders.Short == 0)
            {
                if (DoOpenOrder(TrCurrency, SHORT, TradeSize, 0, 0, Red, "") > 0)
                {
                    alertStr = "Pair:" + Symbol() + ", T/F:" +  getTimeFrame(TrTimeFrame) + ", Direction: SHORT, Price:" + DoubleToStr(Bid, Digits);                   
                     if (Text.Alert)
                     {
                         Alert(alertStr);
                         Print(alertStr);
                     }
                     if (Email.Alert)
                     {
                         SendMail("System 5 SELL to CLOSE and go SHORT GBP at MARKET" , alertStr);  
                     }
                     if (Sound.Alert)
                     {
                         PlaySound(sound_signal_sell);
                     }         
                }
                else
                {
                    return;
                }
            }
        }
      }
      
      lastTrActTime = iTime(TrCurrency, TrTimeFrame, 0);
      lastIntActTime =  iTime(IntCurrency, IntTimeFrame, 0);
   } 
}

string getTimeFrame(int _Period)
{
    switch (_Period)
    {
        case 1: return ("M1");
                break;
        case 5: return ("M5");
                break;
        case 15: return ("M15");
                break;
        case 30: return ("M30");
                break;
        case 60: return ("H1");
                break;
        case 240: return ("H4");
                break;
        case 1440: return ("D1");
                break;
        case 10080: return ("W1");
                break;
        case 43200: return ("Month");
                break;
    }
}

int CountOrders(string symbol) {

   orders.Long = 0;
   orders.Short = 0;
   
   for( int i = OrdersTotal() - 1; i >= 0; i--) {
   
      OrderSelect( i, SELECT_BY_POS);
      
      if( OrderSymbol() == symbol && OrderMagicNumber() == MagicNumber ) {              
         
         if( OrderType() == OP_BUY ) 
         { 
             orders.Long++; 
         }
         if( OrderType() == OP_SELL ) 
         { 
             orders.Short++; 
         }
      }
   }
   
   orders.total = orders.Long + orders.Short;

   return(orders.total);
}

bool isTradingTime()
{
   bool isTT = false;

   datetime tm0 = TimeCurrent()+ GMT.offHour*60*60;
   datetime tm1, tm2;
   string start.time, end.time;

   switch (TimeDayOfWeek(TimeCurrent()+ GMT.offHour*60*60))
   {
       case 0:   
           start.time = Sunday.Start.Time;
           end.time = Sunday.End.Time;
           if (!Sunday.Session.Active) return (false); 
           break;
       case 1:   
           start.time = Monday.Start.Time;
           end.time = Monday.End.Time;
           if (!Monday.Session.Active) return (false); 
           break;
       case 2:   
           start.time = Tuesday.Start.Time;
           end.time = Tuesday.End.Time;
           if (!Tuesday.Session.Active) return (false); 
           break;
       case 3:   
           start.time = Wednesday.Start.Time;
           end.time = Wednesday.End.Time;
           if (!Wednesday.Session.Active) return (false); 
           break;
       case 4:   
           start.time = Thursday.Start.Time;
           end.time = Thursday.End.Time;
           if (!Thursday.Session.Active) return (false); 
           break;
       case 5:   
           start.time = Friday.Start.Time;
           end.time = Friday.End.Time;
           if (!Friday.Session.Active) return (false); 
           break;
       default:
           return (isTT);            
   }
    
   tm1 = StrToTime(TimeToStr(tm0, TIME_DATE) + " " + start.time);
   tm2 = StrToTime(TimeToStr(tm0, TIME_DATE) + " " + end.time);  
      
   if (tm1 <= tm2)
   {     
      isTT = tm1 <= tm0 && tm0 <= tm2 + 60;
   }
   
   return (isTT);
}

//open an order, if failed try again with RETRYCOUNT times
int DoOpenOrder(string symbol, int dir, double volume, int stop, int take, color clr, string comment)  {

   double sl, tp, entryPrice;

   int ticket = 0;   

   double lots = volume;
   string info;
   double dAsk = MarketInfo(symbol, MODE_ASK);
   double dBid = MarketInfo(symbol, MODE_BID);
   int iDigits = MarketInfo(symbol, MODE_DIGITS);
   double dPoint = MarketInfo(symbol, MODE_POINT);
   
   for (int i=0; i<RETRYCOUNT; i++) {
     for (int j=0; (j<50) && IsTradeContextBusy(); j++)
         Sleep(100);
      RefreshRates();   

      switch(dir)  {
         case LONG:               
               entryPrice = NormalizeDouble(dAsk, iDigits);
               
               if (stop != 0) { sl = entryPrice-(stop*dPoint); }
                else { sl = 0; }
                if (take != 0) { tp = entryPrice +(take*dPoint); }
                else { tp = 0; }
                
                
                info = "Type: " + dir + ", \nentryPrice: " + DoubleToStr(entryPrice, iDigits) 
                   + ", \nLots " + DoubleToStr(lots, 2) + " , \nStop: " + DoubleToStr(sl, iDigits)  
                   + ", \nTP: " + DoubleToStr(tp, iDigits);
                Print(info);
                Comment(info);                
               
                ticket = OrderSend(Symbol(), OP_BUY, lots, entryPrice, Slippage, sl, tp, comment, MagicNumber, 0, clr);
                if (ticket < 0)  
                {
                    ticket = OrderSend(Symbol(), OP_BUY, lots, entryPrice, Slippage, 0, 0, comment, MagicNumber, 0, clr);
                    if (ticket > 0)  
                     {
                          if(OrderSelect(ticket, SELECT_BY_TICKET))
                          {
                              modifyOrderByPrice(ticket, OrderOpenPrice(), sl, tp);
                          }
                     }                    
                }                
                
                break;

         case SHORT:
              entryPrice = NormalizeDouble(dBid, iDigits);
              if (stop != 0) { sl = entryPrice+(stop*dPoint); }
                else { sl = 0; }
                if (take != 0) { tp = entryPrice-(take*dPoint); }
                else { tp = 0; }
                
                info = "Type: " + dir + ", \nentryPrice: " + DoubleToStr(entryPrice, iDigits)
                   + ", \nLots " + DoubleToStr(lots, 2) + " , \nStop: " + DoubleToStr(sl, iDigits)  
                   + ", \nTP: " + DoubleToStr(tp, iDigits);
              Print(info);
              Comment(info);
          
                ticket = OrderSend(symbol, OP_SELL, lots, entryPrice, Slippage, sl, tp, comment, MagicNumber, 0, clr);     
                if (ticket < 0)  
                {
                    ticket = OrderSend(symbol, OP_SELL, lots, entryPrice, Slippage, 0, 0, comment, MagicNumber, 0, clr);                    
                     if (ticket > 0)  
                     {
                        if(OrderSelect(ticket, SELECT_BY_TICKET))
                        {
                            modifyOrderByPrice(ticket, OrderOpenPrice(), sl, tp);
                        }
                     }   
                }                                     
                break;
      }
           
      if( ticket > 0 ) { return( ticket ); }
      else {
         Print("DoOpenOrder: error \'"+ErrorDescription(GetLastError())+"\' when opening entry order");
         Sleep(RETRYDELAY);      
      }      
   }
      
   return( -1 );
}

//close all open positions with the specified LONG or SHORT direction
void ExitAll(int direction, string symbol) {       
    for (int i = OrdersTotal() - 1; i >= 0; i--) {
      OrderSelect(i, SELECT_BY_POS, MODE_TRADES);         
    
      if(OrderSymbol() == symbol && OrderMagicNumber() == MagicNumber) {
         if (OrderType() == OP_BUY && direction == LONG) { Exit(OrderTicket(), LONG, OrderLots(), Blue); }
         if (OrderType() == OP_SELL && direction == SHORT) { Exit( OrderTicket(), SHORT, OrderLots(), Red); }
      }
   }
}

//close an order with specifed ticket, if failed try again with RETRYCOUNT times
bool Exit(int ticket, int dir, double volume, color clr, int t = 0)  {
    int i, j, cmd;
    double prc, sl, tp, lots;
    string cmt;

    bool closed;

    Print("Exit("+dir+","+DoubleToStr(volume,3)+","+t+")");

    for (i=0; i<RETRYCOUNT; i++) {
        for (j=0; (j<50) && IsTradeContextBusy(); j++)
            Sleep(100);
        RefreshRates();

        if (dir == LONG) {
            prc = Bid;
        }
        if (dir == SHORT) {
            prc = Ask;
       }
        Print("Exit: prc="+DoubleToStr(prc,Digits));

        closed = OrderClose(ticket,volume,prc,Slippage,clr);
        if (closed) {
            Print("Trade closed");

            return (true);
        }

        Print("Exit: error \'"+ErrorDescription(GetLastError())+"\' when exiting with "+DoubleToStr(volume,3)+" @"+DoubleToStr(prc,Digits));
        Sleep(RETRYDELAY);
    }

    Print("Exit: can\'t enter after "+RETRYCOUNT+" retries");
    return (false);
}

//modify an order with specified ticket, if failed try again with RETRYCOUNT times
bool modifyOrderByPrice(int ticket, double price, double stopLoss, double takeProfit)  
{
   bool retVal = true;     
   
   if(!OrderSelect(ticket, SELECT_BY_TICKET)) return (false);
   
   for (int i=0; i<RETRYCOUNT; i++) 
   {
     for (int j=0; (j<50) && IsTradeContextBusy(); j++)
        Sleep(100);
      RefreshRates();                        

       if (MathAbs(OrderStopLoss() - stopLoss) > Point || MathAbs(OrderTakeProfit() - takeProfit) > Point)
       {              
           retVal = OrderModify(ticket, price, stopLoss, takeProfit, 0);
       }
         
      if(retVal) { return( true ); } 
      else {
         Print("DoModifyOrder: error \'"+ErrorDescription(GetLastError())+"\' when modifying order SL:", stopLoss, " TP:", takeProfit, " OrderOpenPrice:",OrderOpenPrice(), " OrderStopLoss:",OrderStopLoss());
         Sleep(RETRYDELAY);      
      }  
   }      
   return( false );
}

//return error message for each kind of errorCode
string ErrorDescription(int error_code)
{
    string error_string;

    switch( error_code ) {
        case 0:
        case 1:   error_string="no error";                                                  break;
        case 2:   error_string="common error";                                              break;
        case 3:   error_string="invalid trade parameters";                                  break;
        case 4:   error_string="trade server is busy";                                      break;
        case 5:   error_string="old version of the client terminal";                        break;
        case 6:   error_string="no connection with trade server";                           break;
        case 7:   error_string="not enough rights";                                         break;
        case 8:   error_string="too frequent requests";                                     break;
        case 9:   error_string="malfunctional trade operation (never returned error)";      break;
        case 64:  error_string="account disabled";                                          break;
        case 65:  error_string="invalid account";                                           break;
        case 128: error_string="trade timeout";                                             break;
        case 129: error_string="invalid price";                                             break;
        case 130: error_string="invalid stops";                                             break;
        case 131: error_string="invalid trade volume";                                      break;
        case 132: error_string="market is closed";                                          break;
        case 133: error_string="trade is disabled";                                         break;
        case 134: error_string="not enough money";                                          break;
        case 135: error_string="price changed";                                             break;
        case 136: error_string="off quotes";                                                break;
        case 137: error_string="broker is busy (never returned error)";                     break;
        case 138: error_string="requote";                                                   break;
        case 139: error_string="order is locked";                                           break;
        case 140: error_string="long positions only allowed";                               break;
        case 141: error_string="too many requests";                                         break;
        case 145: error_string="modification denied because order too close to market";     break;
        case 146: error_string="trade context is busy";                                     break;
        case 147: error_string="expirations are denied by broker";                          break;
        case 148: error_string="amount of open and pending orders has reached the limit";   break;
        case 149: error_string="hedging is prohibited";                                     break;
        case 150: error_string="prohibited by FIFO rules";                                  break;
        case 4000: error_string="no error (never generated code)";                          break;
        case 4001: error_string="wrong function pointer";                                   break;
        case 4002: error_string="array index is out of range";                              break;
        case 4003: error_string="no memory for function call stack";                        break;
        case 4004: error_string="recursive stack overflow";                                 break;
        case 4005: error_string="not enough stack for parameter";                           break;
        case 4006: error_string="no memory for parameter string";                           break;
        case 4007: error_string="no memory for temp string";                                break;
        case 4008: error_string="not initialized string";                                   break;
        case 4009: error_string="not initialized string in array";                          break;
        case 4010: error_string="no memory for array\' string";                             break;
        case 4011: error_string="too long string";                                          break;
        case 4012: error_string="remainder from zero divide";                               break;
        case 4013: error_string="zero divide";                                              break;
        case 4014: error_string="unknown command";                                          break;
        case 4015: error_string="wrong jump (never generated error)";                       break;
        case 4016: error_string="not initialized array";                                    break;
        case 4017: error_string="dll calls are not allowed";                                break;
        case 4018: error_string="cannot load library";                                      break;
        case 4019: error_string="cannot call function";                                     break;
        case 4020: error_string="expert function calls are not allowed";                    break;
        case 4021: error_string="not enough memory for temp string returned from function"; break;
        case 4022: error_string="system is busy (never generated error)";                   break;
        case 4050: error_string="invalid function parameters count";                        break;
        case 4051: error_string="invalid function parameter value";                         break;
        case 4052: error_string="string function internal error";                           break;
        case 4053: error_string="some array error";                                         break;
        case 4054: error_string="incorrect series array using";                             break;
        case 4055: error_string="custom indicator error";                                   break;
        case 4056: error_string="arrays are incompatible";                                  break;
        case 4057: error_string="global variables processing error";                        break;
        case 4058: error_string="global variable not found";                                break;
        case 4059: error_string="function is not allowed in testing mode";                  break;
        case 4060: error_string="function is not confirmed";                                break;
        case 4061: error_string="send mail error";                                          break;
        case 4062: error_string="string parameter expected";                                break;
        case 4063: error_string="integer parameter expected";                               break;
        case 4064: error_string="double parameter expected";                                break;
        case 4065: error_string="array as parameter expected";                              break;
        case 4066: error_string="requested history data in update state";                   break;
        case 4099: error_string="end of file";                                              break;
        case 4100: error_string="some file error";                                          break;
        case 4101: error_string="wrong file name";                                          break;
        case 4102: error_string="too many opened files";                                    break;
        case 4103: error_string="cannot open file";                                         break;
        case 4104: error_string="incompatible access to a file";                            break;
        case 4105: error_string="no order selected";                                        break;
        case 4106: error_string="unknown symbol";                                           break;
        case 4107: error_string="invalid price parameter for trade function";               break;
        case 4108: error_string="invalid ticket";                                           break;
        case 4109: error_string="trade is not allowed in the expert properties";            break;
        case 4110: error_string="longs are not allowed in the expert properties";           break;
        case 4111: error_string="shorts are not allowed in the expert properties";          break;
        case 4200: error_string="object is already exist";                                  break;
        case 4201: error_string="unknown object property";                                  break;
        case 4202: error_string="object is not exist";                                      break;
        case 4203: error_string="unknown object type";                                      break;
        case 4204: error_string="no object name";                                           break;
        case 4205: error_string="object coordinates error";                                 break;
        case 4206: error_string="no specified subwindow";                                   break;
        default:   error_string="unknown error";
    }

    return(error_string);
}